package wsBroker

import (
	"context"
	"github.com/gorilla/websocket"
	"sync"
	"t11Lib/t11Errors"
	"t11Lib/t11Log"
	"t11Lib/t11Model"
	"亚太-iot-服务器/models"
)

type pktHandler func(*t11Model.DbConn, *WsConn, string) t11Errors.T11Error

type WsConn struct {
	pktHandler pktHandler
	clientId   *models.JwtToken
	sendChan   chan string
	conn       *websocket.Conn
	cancel     context.CancelFunc
	closeOnce  *sync.Once
	hub        *wsHub
	dbConn     *t11Model.DbConn
}

func (self *WsConn) GetClient() *models.JwtToken {
	return self.clientId
}

func (self *WsConn) readRoutine() {
	defer self.close()
	for {
		msgType, msg, err := self.conn.ReadMessage()
		if err != nil {
			t11Log.Debuglnf("websocket read error， err = %v", err)
			return
		}
		if websocket.TextMessage != msgType {
			continue
		}
		if self.pktHandler != nil {
			if err := self.pktHandler(self.dbConn, self, string(msg)); err != nil {
				t11Log.Debuglnf("ws数据包处理失败，err = %v", err)
				return
			}
		}
	}
}

func (self *WsConn) WriteData(data string) t11Errors.T11Error {
	select {
	case self.sendChan <- data:
		return nil
	default:
		return t11Errors.New("websocket当前不可用")
	}
}

func (self *WsConn) writeRoutine(ctx context.Context) {
	for {
		select {
		case data := <-self.sendChan:
			t11Log.Debuglnf("往websocket写入数据：%v", data)
			err := self.conn.WriteMessage(websocket.TextMessage, []byte(data))
			if err != nil {
				self.close()
				return
			}
		case <-ctx.Done():
			return
		}
	}
}

func (self *WsConn) close(unRegisterFromHub ...bool) {
	unRegister := true
	if len(unRegisterFromHub) > 0 {
		unRegister = unRegisterFromHub[0]
	}
	_ = self.conn.Close()
	self.cancel()
	if unRegister {
		self.hub.UnregisterClient(self.clientId)
	}
}

func CreateAndRegisterWsClient(clientId *models.JwtToken, ws *websocket.Conn, hub *wsHub, pktHandler pktHandler) {
	ctx, cancel := context.WithCancel(context.TODO())
	closeOnce := &sync.Once{}
	client := &WsConn{
		conn:       ws,
		hub:        hub,
		cancel:     cancel,
		clientId:   clientId,
		closeOnce:  closeOnce,
		pktHandler: pktHandler,
		sendChan:   make(chan string, 10),
		dbConn:     t11Model.NewDbConn(),
	}
	go client.readRoutine()
	go client.writeRoutine(ctx)
	hub.RegisterClient(clientId, client)
}
